#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
PyCron Web管理界面
基于Flask的定时任务管理Web应用
"""

import os
import sys
import time
from flask import Flask, render_template, request, redirect, url_for, flash, jsonify
from flask_wtf import FlaskForm
from wtforms import StringField, TextAreaField, BooleanField, IntegerField, SelectField
from wtforms.validators import DataRequired, Optional, NumberRange
from datetime import datetime
import logging

# 添加项目根目录到Python路径
current_dir = os.path.dirname(os.path.abspath(__file__))
parent_dir = os.path.dirname(current_dir)
if parent_dir not in sys.path:
    sys.path.insert(0, parent_dir)

# 导入调度器相关模块
from scheduler.task_scheduler import TaskScheduler, ScheduledTask, TaskStatus
from scheduler.config_manager import ConfigManager
from scheduler.cron_parser import CronParser, get_cron_examples

# 创建Flask应用
app = Flask(__name__)
app.config['SECRET_KEY'] = 'pycron-web-secret-key-2023'
app.config['TEMPLATES_AUTO_RELOAD'] = True

# 配置日志
import logging
logging.basicConfig(level=logging.INFO)
app.logger.setLevel(logging.INFO)

# 全局变量
scheduler = None
config_manager = None


def init_app():
    """初始化应用"""
    global scheduler, config_manager

    # 获取配置目录路径
    config_dir = os.environ.get('PYCRON_CONFIG_DIR')
    if not config_dir:
        # 如果没有设置环境变量，计算cronpy根目录的config路径
        cronpy_root = os.path.dirname(current_dir)  # 从web目录向上到cronpy根目录
        config_dir = os.path.join(cronpy_root, 'config')

    # 初始化配置管理器
    config_manager = ConfigManager(config_dir=config_dir)

    # 初始化调度器
    scheduler_config = config_manager.get_scheduler_config()
    scheduler = TaskScheduler(
        max_workers=scheduler_config.get("max_workers", 5),
        log_file=scheduler_config.get("log_file", "scheduler.log")
    )

    # 加载任务
    tasks = config_manager.load_all_tasks()
    for task in tasks:
        try:
            scheduler.add_task(task)
        except Exception as e:
            app.logger.error(f"加载任务 {task.task_id} 失败: {e}")


# 表单定义
class TaskForm(FlaskForm):
    """任务表单"""
    task_id = StringField('任务ID', validators=[DataRequired()])
    name = StringField('任务名称', validators=[DataRequired()])
    cron_expression = StringField('Cron表达式', validators=[DataRequired()])
    command = TextAreaField('执行命令', validators=[DataRequired()])
    description = TextAreaField('任务描述', validators=[Optional()])
    working_dir = StringField('工作目录', validators=[Optional()])
    timeout = IntegerField('超时时间(秒)', validators=[Optional(), NumberRange(min=0)])
    max_retries = IntegerField('最大重试次数', validators=[Optional(), NumberRange(min=0)], default=0)
    enabled = BooleanField('启用任务', default=True)


# 路由定义
@app.route('/')
def index():
    """主页 - 任务列表"""
    try:
        if scheduler is None:
            app.logger.info("调度器未初始化，正在初始化...")
            init_app()

        tasks = []
        if scheduler and hasattr(scheduler, 'list_tasks'):
            try:
                # 确保调度器正在运行，如果没有运行则启动它
                if not scheduler.running:
                    app.logger.info("调度器未运行，正在启动...")
                    scheduler.start()
                    time.sleep(0.1)  # 短暂等待调度器启动

                task_list = scheduler.list_tasks()
                app.logger.debug(f"从调度器获取到 {len(task_list)} 个任务")

                # 如果调度器中没有任务，但配置文件中有任务，重新加载
                if len(task_list) == 0 and config_manager.tasks_config:
                    app.logger.info("调度器中没有任务，但配置文件中有任务，正在重新加载...")
                    for task_config in config_manager.tasks_config:
                        try:
                            task_obj = config_manager.create_task_from_config(task_config)
                            scheduler.add_task(task_obj)
                        except Exception as e:
                            app.logger.error(f"重新加载任务失败 {task_config.get('task_id')}: {e}")

                    # 重新获取任务列表
                    task_list = scheduler.list_tasks()
                    app.logger.info(f"重新加载后获取到 {len(task_list)} 个任务")

                for task in task_list:
                    # 直接使用任务对象而不是字典，这样保留了datetime对象
                    task_data = {
                        'task_id': task.task_id,
                        'name': task.name,
                        'cron_expression': task.cron_expression,
                        'command': task.command,
                        'enabled': task.enabled,
                        'description': task.description,
                        'working_dir': task.working_dir,
                        'environment': task.environment,
                        'timeout': task.timeout,
                        'max_retries': task.max_retries,
                        'last_run_time': task.last_run_time,
                        'next_run_time': task.next_run_time,
                        'status': task.status.value if hasattr(task, 'status') else 'unknown',
                        'retry_count': task.retry_count if hasattr(task, 'retry_count') else 0
                    }

                    # 添加状态信息
                    try:
                        status_info = scheduler.get_task_status(task.task_id) or {}
                        task_data.update(status_info)
                    except Exception as e:
                        app.logger.warning(f"获取任务 {task.task_id} 状态失败: {e}")

                    tasks.append(task_data)

                app.logger.debug(f"准备显示 {len(tasks)} 个任务")
            except Exception as e:
                app.logger.error(f"获取任务列表失败: {e}")
                flash(f"⚠️ 获取任务列表失败: {e}", 'warning')
        else:
            app.logger.warning("调度器不可用")
            flash("⚠️ 调度器不可用，请检查系统状态", 'warning')

    except Exception as e:
        app.logger.error(f"加载主页失败: {e}")
        flash(f"✗ 加载页面失败: {e}", 'error')
        tasks = []

    response = render_template('index.html', tasks=tasks)
    # 添加缓存控制头，防止页面被浏览器缓存
    response = app.make_response(response)
    response.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate'
    response.headers['Pragma'] = 'no-cache'
    response.headers['Expires'] = '0'
    return response


@app.route('/task/add', methods=['GET', 'POST'])
def add_task():
    """添加任务"""
    form = TaskForm()
    if form.validate_on_submit():
        try:
            # 创建任务配置
            task_config = {
                'task_id': form.task_id.data,
                'name': form.name.data,
                'cron_expression': form.cron_expression.data,
                'command': form.command.data,
                'description': form.description.data,
                'working_dir': form.working_dir.data or '',
                'timeout': form.timeout.data,
                'max_retries': form.max_retries.data,
                'enabled': form.enabled.data
            }

            # 验证cron表达式
            try:
                parser = CronParser(form.cron_expression.data)
                flash(f"✓ Cron表达式有效: {parser.get_schedule_description()}", 'success')
            except Exception as e:
                flash(f"✗ Cron表达式无效: {e}", 'error')
                return render_template('add_task.html', form=form)

            # 添加到配置
            config_manager.add_task_config(task_config)
            app.logger.info(f"任务已添加到配置: {task_config['task_id']}")

            # 确保调度器已初始化并启动
            if scheduler is None:
                app.logger.info("调度器未初始化，正在初始化...")
                init_app()

            # 如果调度器存在但没有运行，先启动它
            if scheduler and not scheduler.running:
                app.logger.info("调度器未运行，正在启动...")
                scheduler_config = config_manager.get_scheduler_config()
                scheduler.start()

            # 添加任务到调度器
            if scheduler:
                try:
                    task_obj = config_manager.create_task_from_config(task_config)
                    scheduler.add_task(task_obj)
                    app.logger.info(f"任务已添加到调度器: {task_config['task_id']}")
                except Exception as e:
                    app.logger.error(f"添加任务到调度器失败: {e}")
                    flash(f"⚠️ 任务已保存到配置，但添加到调度器失败: {e}", 'warning')

            app.logger.info(f"任务添加完成: {task_config['task_id']}")
            flash(f"✓ 任务 '{form.name.data}' 添加成功", 'success')
            return redirect(url_for('index'))

        except Exception as e:
            flash(f"✗ 添加任务失败: {e}", 'error')

    return render_template('add_task.html', form=form)


@app.route('/task/<task_id>/edit', methods=['GET', 'POST'])
def edit_task(task_id):
    """编辑任务"""
    # 获取现有配置
    existing_config = config_manager.get_task_config(task_id)
    if not existing_config:
        flash(f"✗ 任务 '{task_id}' 不存在", 'error')
        return redirect(url_for('index'))

    form = TaskForm(data=existing_config)

    if form.validate_on_submit():
        try:
            updates = {
                'name': form.name.data,
                'cron_expression': form.cron_expression.data,
                'command': form.command.data,
                'description': form.description.data,
                'working_dir': form.working_dir.data or '',
                'timeout': form.timeout.data,
                'max_retries': form.max_retries.data,
                'enabled': form.enabled.data
            }

            # 验证cron表达式
            try:
                parser = CronParser(form.cron_expression.data)
                flash(f"✓ Cron表达式有效: {parser.get_schedule_description()}", 'success')
            except Exception as e:
                flash(f"✗ Cron表达式无效: {e}", 'error')
                return render_template('edit_task.html', form=form, task_id=task_id)

            # 更新配置
            config_manager.update_task_config(task_id, updates)

            # 如果调度器正在运行，更新任务
            if scheduler and scheduler.running:
                updated_config = config_manager.get_task_config(task_id)
                if updated_config:
                    task_obj = config_manager.create_task_from_config(updated_config)
                    scheduler.update_task(task_obj)

            flash(f"✓ 任务 '{task_id}' 更新成功", 'success')
            return redirect(url_for('index'))

        except Exception as e:
            flash(f"✗ 更新任务失败: {e}", 'error')

    return render_template('edit_task.html', form=form, task_id=task_id)


@app.route('/task/<task_id>/delete', methods=['POST'])
def delete_task(task_id):
    """删除任务"""
    try:
        app.logger.info(f"开始删除任务: {task_id}")

        # 检查任务是否存在
        task_exists_in_config = any(task.get("task_id") == task_id for task in config_manager.tasks_config)
        task_exists_in_scheduler = scheduler and hasattr(scheduler, 'tasks') and task_id in scheduler.tasks

        app.logger.info(f"删除任务前检查 - 配置中存在: {task_exists_in_config}, 调度器中存在: {task_exists_in_scheduler}")

        # 确保调度器已初始化
        if scheduler is None:
            init_app()
            app.logger.info("调度器已初始化")

        # 从配置中移除
        if task_exists_in_config:
            config_manager.remove_task_config(task_id)
            app.logger.info(f"从配置中删除了任务: {task_id}")
        else:
            app.logger.warning(f"任务 {task_id} 在配置中不存在")

        # 从调度器中移除（无论是否正在运行）
        if scheduler and hasattr(scheduler, 'tasks'):
            if task_id in scheduler.tasks:
                try:
                    scheduler.remove_task(task_id)
                    app.logger.info(f"从调度器中删除了任务: {task_id}")
                except Exception as e:
                    app.logger.error(f"从调度器移除任务失败: {e}")
                    # 继续执行，不要因为这个错误而中断
            else:
                app.logger.info(f"任务 {task_id} 在调度器中不存在")

        # 验证删除是否成功
        remaining_in_config = any(task.get("task_id") == task_id for task in config_manager.tasks_config)
        remaining_in_scheduler = scheduler and hasattr(scheduler, 'tasks') and task_id in scheduler.tasks

        app.logger.info(f"删除任务后检查 - 配置中剩余: {remaining_in_config}, 调度器中剩余: {remaining_in_scheduler}")

        if remaining_in_config or remaining_in_scheduler:
            app.logger.warning(f"任务 '{task_id}' 删除可能不完整")
            flash(f"⚠️ 任务 '{task_id}' 删除可能不完整，请刷新页面后重试", 'warning')
        else:
            app.logger.info(f"任务 '{task_id}' 删除成功")
            flash(f"✓ 任务 '{task_id}' 删除成功", 'success')

    except Exception as e:
        app.logger.error(f"删除任务失败: {e}")
        app.logger.error(f"错误详情: {str(e)}", exc_info=True)
        flash(f"✗ 删除任务失败: {e}", 'error')

    # 添加缓存控制头，确保页面不会被缓存
    response = redirect(url_for('index'))
    response.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate'
    response.headers['Pragma'] = 'no-cache'
    response.headers['Expires'] = '0'
    return response


@app.route('/task/<task_id>/toggle', methods=['POST'])
def toggle_task(task_id):
    """启用/禁用任务"""
    try:
        existing_config = config_manager.get_task_config(task_id)
        if not existing_config:
            flash(f"✗ 任务 '{task_id}' 不存在", 'error')
            return redirect(url_for('index'))

        new_enabled = not existing_config.get('enabled', True)
        config_manager.update_task_config(task_id, {'enabled': new_enabled})

        if scheduler and scheduler.running:
            if new_enabled:
                scheduler.enable_task(task_id)
            else:
                scheduler.disable_task(task_id)

        status_text = "启用" if new_enabled else "禁用"
        flash(f"✓ 任务 '{task_id}' 已{status_text}", 'success')

    except Exception as e:
        flash(f"✗ 操作失败: {e}", 'error')

    return redirect(url_for('index'))


@app.route('/task/<task_id>/run', methods=['POST'])
def run_task(task_id):
    """立即执行任务"""
    try:
        if not scheduler:
            flash("✗ 调度器未初始化", 'error')
            return redirect(url_for('index'))

        task = scheduler.get_task(task_id)
        if not task:
            flash(f"✗ 任务 '{task_id}' 不存在", 'error')
            return redirect(url_for('index'))

        # 立即执行任务
        future = scheduler.executor.submit(scheduler._run_task_async, task)
        # 等待任务完成
        future.result()
        
        flash(f"✓ 任务 '{task.name}' 执行完成", 'success')

    except Exception as e:
        flash(f"✗ 执行任务失败: {e}", 'error')

    return redirect(url_for('index'))


@app.route('/scheduler/start', methods=['POST'])
def start_scheduler():
    """启动调度器"""
    try:
        if scheduler is None:
            init_app()

        if not scheduler.running:
            scheduler.start()
            flash("✓ 调度器已启动", 'success')
        else:
            flash("⚠ 调度器已在运行", 'warning')

    except Exception as e:
        flash(f"✗ 启动调度器失败: {e}", 'error')

    return redirect(url_for('index'))


@app.route('/scheduler/stop', methods=['POST'])
def stop_scheduler():
    """停止调度器"""
    try:
        if scheduler and scheduler.running:
            scheduler.stop()
            flash("✓ 调度器已停止", 'success')
        else:
            flash("⚠ 调度器未运行", 'warning')

    except Exception as e:
        flash(f"✗ 停止调度器失败: {e}", 'error')

    return redirect(url_for('index'))


@app.route('/api/tasks', methods=['GET'])
def api_tasks():
    """API: 获取任务列表"""
    try:
        tasks = []
        if scheduler:
            task_list = scheduler.list_tasks()
            for task in task_list:
                # 为API创建字典，确保日期时间字段正确格式化
                task_dict = {
                    'task_id': task.task_id,
                    'name': task.name,
                    'cron_expression': task.cron_expression,
                    'command': task.command,
                    'enabled': task.enabled,
                    'description': task.description,
                    'working_dir': task.working_dir,
                    'environment': task.environment,
                    'timeout': task.timeout,
                    'max_retries': task.max_retries,
                    'last_run_time': task.last_run_time.isoformat() if task.last_run_time else None,
                    'next_run_time': task.next_run_time.isoformat() if task.next_run_time else None,
                    'status': task.status.value,
                    'retry_count': task.retry_count
                }

                # 添加状态信息
                status_info = scheduler.get_task_status(task.task_id) or {}
                task_dict.update(status_info)
                tasks.append(task_dict)

        return jsonify({'success': True, 'tasks': tasks})
    except Exception as e:
        return jsonify({'success': False, 'error': str(e)}), 500


@app.route('/api/task/<task_id>', methods=['GET'])
def api_task_detail(task_id):
    """API: 获取任务详情"""
    try:
        if not scheduler:
            return jsonify({'success': False, 'error': '调度器未初始化'}), 500

        task = scheduler.get_task(task_id)
        if not task:
            return jsonify({'success': False, 'error': '任务不存在'}), 404

        # 为API创建字典，确保日期时间字段正确格式化
        task_dict = {
            'task_id': task.task_id,
            'name': task.name,
            'cron_expression': task.cron_expression,
            'command': task.command,
            'enabled': task.enabled,
            'description': task.description,
            'working_dir': task.working_dir,
            'environment': task.environment,
            'timeout': task.timeout,
            'max_retries': task.max_retries,
            'last_run_time': task.last_run_time.isoformat() if task.last_run_time else None,
            'next_run_time': task.next_run_time.isoformat() if task.next_run_time else None,
            'status': task.status.value,
            'retry_count': task.retry_count
        }

        # 添加状态信息
        status_info = scheduler.get_task_status(task_id) or {}
        task_dict.update(status_info)

        # 添加执行历史
        task_dict['results'] = []
        for result in task.results[-10:]:  # 最近10个结果
            result_dict = {
                'task_id': result.task_id,
                'status': result.status.value,
                'start_time': result.start_time.isoformat(),
                'end_time': result.end_time.isoformat() if result.end_time else None,
                'output': result.output,
                'error': result.error,
                'exit_code': result.exit_code
            }
            task_dict['results'].append(result_dict)

        return jsonify({'success': True, 'task': task_dict})
    except Exception as e:
        return jsonify({'success': False, 'error': str(e)}), 500


@app.route('/cron/examples')
def cron_examples():
    """Cron表达式示例页面"""
    try:
        examples = get_cron_examples()
        return render_template('cron_examples.html', examples=examples)
    except Exception as e:
        flash(f"获取示例失败: {e}", 'error')
        return redirect(url_for('index'))


if __name__ == '__main__':
    # 初始化应用
    init_app()

    # 启动Web服务器
    app.run(host='0.0.0.0', port=8080, debug=True)
